Rust から C のデータにアクセスする
from 項目34:FFI境界を通過するものを制御しよう
C も Rust も関連データを 1 つのデータ構造にまとめるのに 構造体 を用いる
しかし、各フィールドの値を置くメモリ上の位置や順番(メモリレイアウト)は異なる
https://doc.rust-lang.org/reference/type-layout.html
この不整合を防ぐために、「FFI で用いる Rust の型には #[repr(C)] を付ける」
e.g.
code:lib.h
/// この構造体の変更は、lib.rs にも反映する必要がある
typedef struct {
uint8_t byte;
uint32_t integer;
} FfiStruct;
code:lib.rs
/// この構造体の変更は、lib.h / lib.c にも反映する必要がある
#repr(C)
pub struct FfiStruct {
pub byte: u8,
pub integer: u32,
}
関数の場合と同様、一致していなくてもビルドツールは警告を出さないので、cargo-bindgen を用いて整合していることを CI でチェックすべきである
warning.icon
Rust と C のデフォルトの文字列の定義が全く異なるため、扱う際には注意が必要
デフォルトの定義
Rust(String): UTF-8 でエンコードされたデータを保持する
データには ゼロバイト(\0)を含むことがあるが、その長さを明示的に管理する
したがって、"Hello\0World" という文字列の長さは 11 Byte となる
C(char *): バイト値(符号ありかは不明)を保持する
長さは、データ内で最初に現れるゼロバイトによって暗黙的に決まる
したがって、"Hello\0World" という文字列の長さは 5 Byte となる
この差を埋めるために、
「 C の文字列をバインディングした(所有 された)文字列は CString 型として扱おう」
また、上記に対応する「借用 された文字列値は Cstr 型として扱おう」
Cstr::as_ptr を用いると、C の文字列(const char*)を受け取る FFI 関数に、データを渡すことができる
const なので、渡された文字列の内容(char*)を変更する必要がある場合には使えない
#Rust #Effective_Rust_―_Rustコードを改善し、エコシステムを最大限に活用するための35項目